# Copyright (c) Prophesee S.A.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and limitations under the License.

# Find MetavisionSDK libraries
#
# Use this module by invoking find_package with the form::
#
#   find_package(MetavisionSDK
#     [version] [EXACT]      # Minimum or EXACT version e.g. 1.5.0
#     [REQUIRED]             # Fail with error if MetavisionSDK is not found
#     [COMPONENTS <c>...]    # MetavisionSDK module(s) by their canonical name
#     )                      # e.g. "cv" for "libmetavision_sdk_cv"
#
#
# This module finds headers and requested component libraries of the MetavisionSDK package.
# Results are reported in variables::
#
#   MetavisionSDK_FOUND                 - True if headers and requested libraries were found
#   MetavisionSDK_LIBRARIES             - MetavisionSDK component targets to be linked
#   MetavisionSDK_<c>_FOUND             - True if component <c> was found
#   MetavisionSDK_<c>_LIBRARY           - Targets to link for component <c>
#   MetavisionSDK_VERSION               - MetavisionSDK version value from metavision/sdk/version.h
#   MetavisionSDK_VERSION_MAJOR         - MetavisionSDK major version number (X in X.y.z)
#   MetavisionSDK_VERSION_MINOR         - MetavisionSDK minor version number (Y in x.Y.z)
#   MetavisionSDK_VERSION_PATCH         - MetavisionSDK patch version number (Z in x.y.Z)
#
#
# Implicit dependencies such as MetavisionSDK::stream requiring
# MetavisionSDK::base will be automatically detected and satisfied, even
# if base is not specified when using find_package and if
# MetavisionSDK::base is not added to target_link_libraries.
#
#
# Example to find MetavisionSDK libraries and use imported targets::
#
#   find_package(MetavisionSDK 5.0.0 REQUIRED COMPONENTS base stream)
#   add_executable(foo foo.cpp)
#   target_link_libraries(foo MetavisionSDK::base MetavisionSDK::stream) # or target_link_libraries(foo ${MetavisionSDK_LIBRARIES})


@PACKAGE_INIT@


# Extract the directory where *this* file has been installed (determined at cmake run-time)
# Get the absolute path with no ../.. relative marks, to eliminate implicit linker warnings
get_filename_component(MetavisionSDK_CONFIG_PATH "${CMAKE_CURRENT_LIST_DIR}/" REALPATH)

# Set the path where to look for the modules
set(MetavisionSDK_COMPONENTS_CONFIG_PATH "${MetavisionSDK_CONFIG_PATH}/@MetavisionSDK_MODULES_CMAKE_CONFIG_SUBDIR@")

# Save project's policies
cmake_policy(PUSH)
cmake_policy(SET CMP0057 NEW) # if IN_LIST

#######################################################################################################
#
#                                    MetavisionSDK utils functions
#
#######################################################################################################

#####################################################################
#
# Gets the list of all subdiretories in a given directory
#
#
# usage :
#     _get_subdirectories(<parent_directory> <output_varname>)
#
#
function(_get_subdirectories parent_dir output_var)
    file(GLOB children RELATIVE ${parent_dir} ${parent_dir}/*)
    set(dirlist "")
    foreach(child ${children})
        if(IS_DIRECTORY ${parent_dir}/${child})
            list(APPEND dirlist ${child})
        endif(IS_DIRECTORY ${parent_dir}/${child})
    endforeach(child)
    set(${output_var} ${dirlist} PARENT_SCOPE)
endfunction(_get_subdirectories)


#####################################################################
#
# Gets the path to files related to a MetavisionSDK component
#
#
# usage :
#     _get_MetavisionSDK_component_filepath(<component> [CONFIG | VERSION | DEPENDENCIES | DIRECTORY] <output_varname>)
#
#
function(_get_MetavisionSDK_component_filepath _comp filecomp output_varname)

    set(_comp_config_path)
    if (EXISTS "${MetavisionSDK_COMPONENTS_CONFIG_PATH_LOCAL}/${_comp}/MetavisionSDK_${_comp}Config.cmake")
        set(_comp_config_path "${MetavisionSDK_COMPONENTS_CONFIG_PATH_LOCAL}")
    else()
        set(_comp_config_path "${MetavisionSDK_COMPONENTS_CONFIG_PATH}")
    endif (EXISTS "${MetavisionSDK_COMPONENTS_CONFIG_PATH_LOCAL}/${_comp}/MetavisionSDK_${_comp}Config.cmake")

    if ("${filecomp}" STREQUAL "CONFIG")
        set (${output_varname} "${_comp_config_path}/${_comp}/MetavisionSDK_${_comp}Config.cmake" PARENT_SCOPE)
    elseif("${filecomp}" STREQUAL "VERSION")
        set (${output_varname} "${_comp_config_path}/${_comp}/MetavisionSDK_${_comp}ConfigVersion.cmake" PARENT_SCOPE)
    elseif("${filecomp}" STREQUAL "DEPENDENCIES")
        set (${output_varname} "${_comp_config_path}/${_comp}/MetavisionSDK_${_comp}Depends.cmake" PARENT_SCOPE)
    elseif("${filecomp}" STREQUAL "DIRECTORY")
        set (${output_varname} "${_comp_config_path}/${_comp}" PARENT_SCOPE)
    else()
        message(FATAL_ERROR "ERROR : unknown option ${filecomp} given to function _get_MetavisionSDK_component_filepath")
    endif()
endfunction(_get_MetavisionSDK_component_filepath)


#####################################################################
#
# Gets the version of a MetavisionSDK component. If version is not found, it's set to "UNKNWON"
#
#
# usage :
#     _get_MetavisionSDK_component_version(<component> <output_varname>)
#
#
function(_get_MetavisionSDK_component_version _comp filecomp output_varname)
    _get_MetavisionSDK_component_filepath("${_comp}" VERSION component_version_filepath)
   if(EXISTS "${component_version_filepath}")
       include("${component_version_filepath}")
       set(${output_varname} "${PACKAGE_VERSION}" PARENT_SCOPE)
   else()
       set(${output_varname} "UNKNWON" PARENT_SCOPE)
   endif(EXISTS "${component_version_filepath}")
endfunction(_get_MetavisionSDK_component_version)


#####################################################################
#
# Finds Metavision SDK component's dependencies
#
# This function looks for the additional components needed by a MetavisionSDK component, and
# it stores them in the output variable passed to the function
#
#
# usage :
#     _find_MetavisionSDK_component_dependencies(<component> <output_varname>)
#
#
# Remark : the list obtained is ordered in a way such that each element on the list can depend only
# from an components listed BEFORE itsself in the list
#
#
function(_find_MetavisionSDK_component_dependencies _comp output_varname)

    # Define a recursive function to get all the dependencies chain :
    set (_deps_already_checked_list "")
    function(_get_deps _current_component)

        if("${_current_component}" IN_LIST _deps_already_checked_list)
            return()
        endif()


        _get_MetavisionSDK_component_filepath("${_current_component}" DEPENDENCIES component_deps_path)
        if (EXISTS "${component_deps_path}")
            # The file is of the following form :
            # set(MetavisionSDK_<c>_NEEDED_COMPONENTS comp1 comp2 ..)
            include("${component_deps_path}")

            foreach(_dep ${MetavisionSDK_${_current_component}_NEEDED_COMPONENTS})
                _get_deps("${_dep}")
            endforeach(_dep)

        endif (EXISTS "${component_deps_path}")

        list(APPEND _deps_already_checked_list "${_current_component}")
        set (_deps_already_checked_list "${_deps_already_checked_list}" PARENT_SCOPE)

    endfunction(_get_deps)

    _get_deps("${_comp}")

    # Remove _comp from the dependencies
    list(REMOVE_ITEM _deps_already_checked_list ${_comp})

    # Sent output variable back to the function's parent scope
    set (${output_varname} "${_deps_already_checked_list}" PARENT_SCOPE)

endfunction(_find_MetavisionSDK_component_dependencies)


#####################################################################
#
# Finds all Metavision SDK available components
#
# This function looks for all MetavisionSDK available components for a given version
#
#
# usage :
#     _find_all_MetavisionSDK_available_components(<version_needed> <output_varname>)
#
#
function(_find_all_MetavisionSDK_available_components version_needed output_varname)

    # Initialize output variable to empty string
    set (${output_varname} "")

    _get_subdirectories("${MetavisionSDK_COMPONENTS_CONFIG_PATH_LOCAL}" local_subdirs)
    _get_subdirectories("${MetavisionSDK_COMPONENTS_CONFIG_PATH}" subdirs)

    foreach(_comp ${local_subdirs} ${subdirs})

        if(NOT "${_comp}" IN_LIST ${output_varname})

            # We consider a component is available if we can find its config file and version config file
            _get_MetavisionSDK_component_filepath("${_comp}" CONFIG component_config_path)
            if (EXISTS "${component_config_path}")
                _get_MetavisionSDK_component_filepath("${_comp}" VERSION component_configversion_path)
                if (EXISTS "${component_configversion_path}")
                    # Check the version is the required one
                    include("${component_configversion_path}")
                    if (${PACKAGE_VERSION} VERSION_EQUAL ${version_needed})
                        list(APPEND ${output_varname} ${_comp})
                    endif (${PACKAGE_VERSION} VERSION_EQUAL ${version_needed})
                endif (EXISTS "${component_configversion_path}")
            endif(EXISTS "${component_config_path}")

        endif(NOT "${_comp}" IN_LIST ${output_varname})

    endforeach(_comp)

    # Send variable back to outside the function scope
    set (${output_varname} "${${output_varname}}" PARENT_SCOPE)

endfunction(_find_all_MetavisionSDK_available_components)



#######################################################################################################
#                                                                                                     #
#                                     Find package MetavisionSDK                                      #
#                                                                                                     #
#######################################################################################################

############################################################################
#            Extract version information from @VERSION_BARE_FILE@
############################################################################

# Extract MetavisionSDK_VERSION from @VERSION_BARE_FILE@
set(_MetavisionSDK_VERSION_HEADER_FILEPATH "${PACKAGE_PREFIX_DIR}/@VERSION_FILE_INSTALLATION_PATH_RELATIVE@/@VERSION_BARE_FILE@")
set(_error_message "")
if (EXISTS ${_MetavisionSDK_VERSION_HEADER_FILEPATH})

    file(STRINGS "${_MetavisionSDK_VERSION_HEADER_FILEPATH}" _MetavisionSDK_VERSION_H_CONTENTS REGEX "#define METAVISION_SDK_VERSION_(.*) ")

    set(_missing_version_components "")
    foreach(v MAJOR MINOR PATCH)
        if("${_MetavisionSDK_VERSION_H_CONTENTS}" MATCHES "#define METAVISION_SDK_VERSION_${v} ([0-9]+)")
            set(MetavisionSDK_VERSION_${v} "${CMAKE_MATCH_1}")
        else()
            set(_missing_version_components "${_missing_version_components} ${v} ")
        endif()
    endforeach(v)

    if(_missing_version_components)
        set(_error_message "could not extract ${_missing_version_components} VERSION for Package MetavisionSDK - corrupted file ${_MetavisionSDK_VERSION_HEADER_FILEPATH} ?")
    else()
        set(MetavisionSDK_VERSION ${MetavisionSDK_VERSION_MAJOR}.${MetavisionSDK_VERSION_MINOR}.${MetavisionSDK_VERSION_PATCH})
    endif(_missing_version_components)

else()

    set(_error_message "could not extract ${v} VERSION for Package MetavisionSDK - missing file ${_MetavisionSDK_VERSION_HEADER_FILEPATH}")

endif (EXISTS ${_MetavisionSDK_VERSION_HEADER_FILEPATH})

if(_error_message)
    set(MetavisionSDK_FOUND False)
    if(MetavisionSDK_FIND_REQUIRED)
        message(SEND_ERROR "Unable to find the requested MetavisionSDK libraries because ${_error_message}")
    else()
        if(NOT MetavisionSDK_FIND_QUIETLY)
            message(STATUS "Could NOT find MetavisionSDK (${_error_message})")
        endif(NOT MetavisionSDK_FIND_QUIETLY)
    endif(MetavisionSDK_FIND_REQUIRED)
    cmake_policy(POP)
    return()
endif(_error_message)

############################################################################
#                 Create a list of the components to find
############################################################################
# Remove the duplicates from the list.
list(REMOVE_DUPLICATES MetavisionSDK_FIND_COMPONENTS)
if (NOT MetavisionSDK_FIND_COMPONENTS)

    # If no component was specifically requested, then find all available ones
    _find_all_MetavisionSDK_available_components("${MetavisionSDK_VERSION}" _MetavisionSDK_components_to_look_for)

else()

    set(_MetavisionSDK_components_to_look_for ${MetavisionSDK_FIND_COMPONENTS})

endif (NOT MetavisionSDK_FIND_COMPONENTS)

# Add the dependencies to the list of the components to look for :
set(_extra_components_needed "")
foreach(_comp ${_MetavisionSDK_components_to_look_for})
    _find_MetavisionSDK_component_dependencies("${_comp}" _comp_deps)
    foreach(_dep ${_comp_deps})
        if(NOT "${_dep}" IN_LIST _MetavisionSDK_components_to_look_for)
            if(NOT "${_dep}" IN_LIST _extra_components_needed)
                list(APPEND _extra_components_needed ${_dep})
            endif(NOT "${_dep}" IN_LIST _extra_components_needed)
        endif(NOT "${_dep}" IN_LIST _MetavisionSDK_components_to_look_for)
    endforeach(_dep)
endforeach()
list(APPEND _MetavisionSDK_components_to_look_for ${_extra_components_needed})

# To ease later processing, order the list _MetavisionSDK_components_to_look_for in a way such that
# any element of the list depends only from components that are BEFORE them in the list
# and set variables _MetavisionSDK_${_comp}_DEPS
set(_MetavisionSDK_components_to_look_for_ordered)
foreach(_comp ${_MetavisionSDK_components_to_look_for})
    if(NOT "${_comp}" IN_LIST _MetavisionSDK_components_to_look_for_ordered)
        _find_MetavisionSDK_component_dependencies("${_comp}" _MetavisionSDK_${_comp}_DEPS)

        # Since function _find_MetavisionSDK_component_dependencies already returns an ordered list
        # we just need to iterate over _MetavisionSDK_${_comp}_DEPS
        foreach(_dep ${_MetavisionSDK_${_comp}_DEPS})
            if(NOT "${_dep}" IN_LIST _MetavisionSDK_components_to_look_for_ordered)
                list(APPEND _MetavisionSDK_components_to_look_for_ordered ${_dep})
            endif(NOT "${_dep}" IN_LIST _MetavisionSDK_components_to_look_for_ordered)
        endforeach(_dep)
        list(APPEND _MetavisionSDK_components_to_look_for_ordered ${_comp})
    endif(NOT "${_comp}" IN_LIST _MetavisionSDK_components_to_look_for_ordered)
endforeach()


############################################################################
#                      Find the requested components
############################################################################

function(_find_MetavisionSDK_component component_name)
    # The configuration files for the components are to be looked for in path ${MetavisionSDK_COMPONENTS_CONFIG_PATH}/${component_name},
    # and their config file is named "MetavisionSDK_${_comp}Config.cmake", so we can use the cmake built-in function find_package()
    # to check if we can find the component
    _get_MetavisionSDK_component_filepath("${component_name}" DIRECTORY MetavisionSDK_${component_name}_DIR)
    find_package(MetavisionSDK_${component_name}
                 NO_DEFAULT_PATH
                 QUIET)
    set(MetavisionSDK_${component_name}_FOUND ${MetavisionSDK_${component_name}_FOUND} PARENT_SCOPE)
    set(MetavisionSDK_${component_name}_VERSION ${MetavisionSDK_${component_name}_VERSION} PARENT_SCOPE)
    set(MetavisionSDK_${component_name}_REASON_NOT_FOUND ${MetavisionSDK_${component_name}_REASON_NOT_FOUND} PARENT_SCOPE)
endfunction(_find_MetavisionSDK_component)

# Find required components
foreach(_comp ${_MetavisionSDK_components_to_look_for_ordered})

    _find_MetavisionSDK_component(${_comp})

    if (MetavisionSDK_${_comp}_FOUND)

        # Check if the found version is the same as the version of MetavisionSDK package (to avoid including incompatible versions)

        if (${MetavisionSDK_${_comp}_VERSION} VERSION_EQUAL ${MetavisionSDK_VERSION})

            set(MetavisionSDK_${_comp}_FOUND True)

            # Check if we found the components dependencies (we can do it already because the list is ordered)

            set(_missing_deps "")
            foreach(_dep ${_MetavisionSDK_${_comp}_DEPS}) # Variable _MetavisionSDK_${_comp}_DEPS stores was set above
                if (NOT MetavisionSDK_${_dep}_FOUND)
                    set(MetavisionSDK_${_comp}_FOUND False)
                    list(APPEND _missing_deps ${_dep})
                endif (NOT MetavisionSDK_${_dep}_FOUND)
            endforeach(_dep)
            if (NOT MetavisionSDK_${_comp}_FOUND)
                set(_MetavisionSDK_${_comp}_REASON_NOT_FOUND "could not find the following components it depends on :")
                foreach(_missing_dep ${_missing_deps})
                    set(_MetavisionSDK_${_comp}_REASON_NOT_FOUND "${_MetavisionSDK_${_comp}_REASON_NOT_FOUND} ${_missing_dep}")
                endforeach(_missing_dep)
            endif (NOT MetavisionSDK_${_comp}_FOUND)


        else()
            set(MetavisionSDK_${_comp}_FOUND False)
            set(_MetavisionSDK_${_comp}_REASON_NOT_FOUND "found version ${MetavisionSDK_${_comp}_VERSION}, but need version ${MetavisionSDK_VERSION}")
        endif (${MetavisionSDK_${_comp}_VERSION} VERSION_EQUAL ${MetavisionSDK_VERSION})

    else()
        set(_MetavisionSDK_${_comp}_REASON_NOT_FOUND "${MetavisionSDK_${_comp}_NOT_FOUND_MESSAGE}")
        # TODO : get better log on why the component was not found (for examples of imported target refers to an unknown target it's because a file *Depends is missing)
        set(MetavisionSDK_${_comp}_FOUND False)
    endif (MetavisionSDK_${_comp}_FOUND)

endforeach(_comp)

############################################################################
#                        Check required components
############################################################################

# Check required components
if (NOT _MetavisionSDK_components_to_look_for_ordered)
    set(MetavisionSDK_FOUND False)
else()
    set(MetavisionSDK_FOUND True)
    check_required_components(MetavisionSDK)
endif (NOT _MetavisionSDK_components_to_look_for_ordered)

############################################################################
#          Include targets and set MetavisionSDK_LIBRARIES
############################################################################
set(MetavisionSDK_LIBRARIES "")
if(MetavisionSDK_FOUND)

    set(_MetavisionSDK_components_found "")
    set(_MetavisionSDK_components_not_found "") # Even if MetavisionSDK_FOUND, it could happen that some components were not found if they were not required

    foreach(_comp ${_MetavisionSDK_components_to_look_for_ordered})
        if (MetavisionSDK_${_comp}_FOUND)
            _get_MetavisionSDK_component_filepath("${_comp}" CONFIG component_config_path)
            include("${component_config_path}")

            list(APPEND MetavisionSDK_LIBRARIES MetavisionSDK::${_comp})
            set(MetavisionSDK_${_comp}_LIBRARY MetavisionSDK::${_comp})

            list(APPEND _MetavisionSDK_components_found ${_comp})
        else()
            list(APPEND _MetavisionSDK_components_not_found ${_comp})
        endif (MetavisionSDK_${_comp}_FOUND)
    endforeach(_comp)

    # Print messages information
    if(NOT MetavisionSDK_FIND_QUIETLY)
        message(STATUS "Found MetavisionSDK : ${PACKAGE_PREFIX_DIR} (found version: ${MetavisionSDK_VERSION})")
        message(STATUS "Found the following MetavisionSDK libraries:")

        foreach(_comp ${_MetavisionSDK_components_found})
            message (STATUS "  ${_comp}")
        endforeach(_comp)

        if (_MetavisionSDK_components_not_found)
            message(STATUS "Could NOT find the following MetavisionSDK libraries:")
            foreach(_comp ${_MetavisionSDK_components_not_found})
                if(_MetavisionSDK_${_comp}_REASON_NOT_FOUND)
                    message (STATUS "  ${_comp} (${_MetavisionSDK_${_comp}_REASON_NOT_FOUND})")
                else()
                    message (STATUS "  ${_comp}")
                endif(_MetavisionSDK_${_comp}_REASON_NOT_FOUND)
            endforeach(_comp)
        endif(_MetavisionSDK_components_not_found)

    endif(NOT MetavisionSDK_FIND_QUIETLY)

else()
    if(MetavisionSDK_FIND_REQUIRED)
        set(err_msg)

        if(MetavisionSDK_FIND_COMPONENTS)
            set(err_msg "Unable to find the requested MetavisionSDK libraries:")
            foreach(_comp ${MetavisionSDK_FIND_COMPONENTS})
                if(NOT MetavisionSDK_${_comp}_FOUND)
                    if(MetavisionSDK_FIND_REQUIRED_${_comp})
                        set(err_msg "${err_msg}\n    ${_comp}")
                        if(_MetavisionSDK_${_comp}_REASON_NOT_FOUND)
                            set(err_msg "${err_msg} (${_MetavisionSDK_${_comp}_REASON_NOT_FOUND})")
                        endif(_MetavisionSDK_${_comp}_REASON_NOT_FOUND)
                    endif(MetavisionSDK_FIND_REQUIRED_${_comp})
                endif()
            endforeach(_comp)
        else()
            set(err_msg "Unable to find MetavisionSDK")
            if (NOT _MetavisionSDK_components_to_look_for_ordered)
                set(err_msg "${err_msg} : no component available ")
            endif (NOT _MetavisionSDK_components_to_look_for_ordered)
        endif(MetavisionSDK_FIND_COMPONENTS)

        message(SEND_ERROR "${err_msg}\n")

    else()
        if(NOT MetavisionSDK_FIND_QUIETLY)
            message(STATUS "Could NOT find MetavisionSDK")
        endif(NOT MetavisionSDK_FIND_QUIETLY)
    endif(MetavisionSDK_FIND_REQUIRED)
endif(MetavisionSDK_FOUND)

if (NOT CMAKE_BUILD_TYPE)
    message(WARNING "Beware your built type isn't set and it may result in your app running slow."
    "\nIf this is not intended, please use `-DCMAKE_BUILD_TYPE=Release` when invoking CMake to ensure good performances")
endif()

cmake_policy(POP)
